/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import $ from 'jquery';
import 'jquery-ui/ui/widgets/sortable.js';
import { defer, isEmpty } from 'lodash';
import {
  Alignment,
  Button,
  Card,
  Icon,
  Menu,
  MenuDivider,
  MenuItem,
  Popover,
  Position
} from '@blueprintjs/core';
import classNames from 'classnames';
import TruncateString from 'react-truncate-string';

// Utils
import { Saiku } from '../../../../utils';

// Styles
import './AxisDimensions.css';

// Constants
const SKU_DROP_DESTINATION_ID = 'SKU_DROP_DESTINATION_ID';

class AxisDimensions extends Component {
  componentDidMount() {
    this.makeSortable();
  }

  componentDidUpdate(prevProps, prevState) {
    this.makeSortable();
  }

  componentWillUnmount() {
    if (this.$rootNode) {
      this.$rootNode.sortable('destroy');
    }
  }

  makeSortable() {
    const { onDragEnd } = this.props;

    defer(() => {
      this.$rootNode = $(this.rootNode).find('.sku-connectable');
      const type = this.$rootNode.attr('type');
      const destination = {
        droppableId: this.$rootNode.attr('dropdestinationid')
      };

      this.$rootNode.sortable({
        connectWith: $('.sku-axis-dimensions'),
        containment: $('.sku-query-designer-dialog'),
        items: '> li',
        opacity: 0.6,
        placeholder: 'placeholder',
        tolerance: 'pointer',
        start: (event, ui) => {
          const isHierGroupClassName = ui.helper
            .find('.sku-hier-group')
            .hasClass('sku-hier-group');
          const $sourceItem = ui.item.find('.sku-level-data');
          const text = isHierGroupClassName
            ? ui.helper.find('.sku-hier-group').text()
            : `${$sourceItem.attr('dimension')} → ${$sourceItem.attr(
                'hierarchy'
              )}`;

          ui.placeholder.text(text);
        },
        receive: (event, ui) => {
          // This event is triggered when an item from a connected sortable list
          // has been dropped into another list. The latter is the event target.
          // Source: http://api.jqueryui.com/sortable/#event-receive

          if (ui.item.hasClass('ui-draggable')) {
            ui.item.draggable('disable');
          }

          this.setDropDestinationId();
        },
        beforeStop: (event, ui) => {
          this.setDropDestinationId();
        },
        stop: (event, ui) => {
          const $levelItem = ui.item.hasClass('ui-draggable')
            ? ui.item.find('.sku-level-data')
            : ui.item;
          const dragSourceId = $levelItem.attr('dragsourceid');
          const source = {
            draggableId:
              dragSourceId === 'TREE_DIMENSIONS'
                ? dragSourceId
                : destination.droppableId,
            index: Number($levelItem.attr('dragsourceindex'))
          };
          const level = {
            dimension: $levelItem.attr('dimension'),
            hierarchyUniqueName: $levelItem.attr('hierarchyuniquename'),
            hierarchy: $levelItem.attr('hierarchy'),
            name: $levelItem.attr('level')
          };
          const newIndex = ui.item.index();

          destination.droppableId = localStorage.getItem(
            SKU_DROP_DESTINATION_ID
          );
          destination.index = newIndex;

          // Reverse everything as it was before dragging
          // https://github.com/facebook/react/issues/9134
          if (
            dragSourceId !== 'TREE_DIMENSIONS' &&
            dragSourceId !== destination.droppableId
          ) {
            this.$rootNode.sortable('cancel');
          }

          // Remove element from parent inside of sortable
          if (
            (dragSourceId === 'TREE_DIMENSIONS' &&
              ui.item.hasClass('ui-draggable')) ||
            destination.index === -1
          ) {
            ui.item.remove();
          }

          onDragEnd({
            type,
            data: level,
            source,
            destination
          });
        }
      });
    });
  }

  setDropDestinationId = () => {
    // TODO: This is just a hack for get the droppable id.
    //       Update this after...
    const $rootNode = $(this.rootNode).find('.sku-connectable');
    const rootNodeDroppableId = $rootNode.attr('dropdestinationid');
    const localStorageDroppableId = localStorage.getItem(
      SKU_DROP_DESTINATION_ID
    );

    if (rootNodeDroppableId !== localStorageDroppableId) {
      localStorage.setItem(SKU_DROP_DESTINATION_ID, rootNodeDroppableId);
    }
  };

  hasDimensions = () => isEmpty(this.props.dimensions);

  handleSwapAxes = () => {
    this.props.swapAxes();
  };

  handleClearAxis = () => {
    const { droppableId, clearAxis } = this.props;
    const axis = droppableId;

    clearAxis(axis);
  };

  handleShowLevels = event => {
    const { showLevels } = this.props;
    const source = {
      draggableId: event.target.getAttribute('dragsourceid'),
      index: event.target.getAttribute('dragsourceindex')
    };

    showLevels(source);
  };

  renderMenuItems() {
    const { title } = this.props;

    return (
      <Menu>
        <MenuItem text="Filter">
          <MenuItem text="Custom Filter" />
          <MenuItem text="Clear Filter" />
        </MenuItem>
        <MenuItem text="Limit">
          <MenuItem text="Top 10" />
          <MenuItem text="Bottom 10" />
          <MenuItem text="Top 10 by..." />
          <MenuItem text="Bottom 10 by..." />
          <MenuItem text="Custom Limit" />
          <MenuItem text="Clear Limit" />
        </MenuItem>
        <MenuItem text="Sort">
          <MenuItem text="Ascending" />
          <MenuItem text="Descending" />
          <MenuItem text="Ascending (Breaking Hierarchy)" />
          <MenuItem text="Descending (Breaking Hierarchy)" />
          <MenuItem text="Custom Sort" />
          <MenuItem text="Clear Sort" />
        </MenuItem>
        <MenuItem text="Totals">
          <MenuItem text="All">
            <MenuItem text="None" />
            <MenuItem text="Sum" />
            <MenuItem text="Min" />
            <MenuItem text="Max" />
            <MenuItem text="Avg" />
          </MenuItem>
        </MenuItem>
        <MenuDivider />
        {title !== 'Filter' && (
          <MenuItem
            text="Swap Axis"
            labelElement={<Icon icon="swap-horizontal" />}
            onClick={this.handleSwapAxes}
          />
        )}
        <MenuItem
          text="Clear Axis"
          onClick={this.handleClearAxis}
          disabled={this.hasDimensions()}
        />
        <MenuItem text="Cancel" />
      </Menu>
    );
  }

  renderHierarchiesItems() {
    const { droppableId, dimensions } = this.props;

    return dimensions.map((hierarchy, index) => (
      <li
        key={Saiku.uid(hierarchy.name)}
        className="sku-level-item"
        dragsourceid={droppableId}
        dragsourceindex={index}
        dimension={hierarchy.dimension}
        hierarchyuniquename={hierarchy.name}
        hierarchy={hierarchy.caption}
        onClick={this.handleShowLevels}
        style={{ opacity: '1', width: '220px', height: '24px' }}
      >
        <TruncateString
          className="sku-hier-group"
          dragsourceid={droppableId}
          dragsourceindex={index}
          text={`${hierarchy.dimension} → ${hierarchy.caption}`}
        />
      </li>
    ));
  }

  render() {
    const { type, droppableId, title, axisClassName } = this.props;

    return (
      <Card title={title}>
        <div className="sku-card-header">
          <Popover
            content={this.renderMenuItems()}
            position={Position.BOTTOM}
            minimal
            autoFocus={false}
            enforceFocus={false}
          >
            <Button
              className="sku-fields-list"
              alignText={Alignment.LEFT}
              text={title}
              rightIcon="caret-down"
              fill
            />
          </Popover>
        </div>
        <div className="sku-card-body" ref={node => (this.rootNode = node)}>
          <ul
            className={classNames(
              axisClassName,
              'sku-connectable sku-axis-dimensions'
            )}
            type={type}
            dropdestinationid={droppableId}
          >
            {this.renderHierarchiesItems()}
          </ul>
        </div>
      </Card>
    );
  }
}

AxisDimensions.propTypes = {
  type: PropTypes.string.isRequired,
  droppableId: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  axisClassName: PropTypes.string.isRequired,
  dimensions: PropTypes.array,
  onDragEnd: PropTypes.func.isRequired,
  showLevels: PropTypes.func.isRequired,
  swapAxes: PropTypes.func,
  clearAxis: PropTypes.func.isRequired
};

export default AxisDimensions;
